﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Text;
using ScrumTable.DL.Data.Common;

#endregion

namespace ScrumTable.BL.DM.DataManagement.Tracking.EditCommands
{
    /// <summary>
    /// Contains all required information to perform undo / redo actions
    /// on multiple child action.
    /// </summary>
    internal class MultipleTrackingUndoCommand : UndoMultipleTrackingCommandBase
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private readonly Action _doBeforeUndoRedo;
        private readonly Action _doAfterUndoRedo;
        private readonly string _elementName;
        private readonly List<IUndoEditCommand> _childActions = new List<IUndoEditCommand>();
        private readonly Dictionary<string, int> _childActionCauses = new Dictionary<string, int>();
        private readonly Dictionary<string, int> _childElementNames = new Dictionary<string, int>();
        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the state change cause as ";" seperated string of all causes if there is only one element effected. 
        /// If more elements are effect only the numbers of effected elements gets returned.
        /// </summary>
        public override string Cause
        {
            get
            {
                return (_childElementNames.Count > 1 || (_childActionCauses.Keys.Count == 1 && _childElementNames.Count > 1) ) ?
                    _childElementNames.Keys.Count.ToString() :
                    StringUtil.Join(_childActionCauses.Keys, "; ");
            }
        }
   

        /// <summary>
        /// Gets the change type which means the element or action that
        /// caused the undo edit entry.
        /// </summary>
        /// <value></value>
        public override UndoEditType Type { get { return UndoEditType.Multiple; } }

        /// <summary>
        /// Gets/sets the associated domain context.
        /// </summary>
        public override IDomainContext Context { get; set; }

        /// <summary>
        /// If more than one element is effected : This MultipleTrackingUndoCommand action name otherwise the first child element name.
        /// </summary>
        public override string ElementName
        {
            get
            {
                if (HasSubEdits)
                {
                    return _childElementNames.Count > 1 ? _elementName : _childActions[0].ElementName;
                }
                return null;
            }
        }

        /// <summary>
        /// Returns true, if the current edit contains sub edit instances.
        /// </summary>
        /// <value></value>
        internal override bool HasSubEdits
        {
            get { return (_childActions.Count > 0); }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new MultipleTrackingUndoCommand instance.
        /// </summary>
        internal MultipleTrackingUndoCommand(
            string actionName,
            Action doBeforeUndoRedo,
            Action doAfterUndoRedo)
        {
            _elementName = actionName;
            _doBeforeUndoRedo = doBeforeUndoRedo;
            _doAfterUndoRedo = doAfterUndoRedo;
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Executes the underlying undo command.
        /// </summary>
        public override void CallUndo()
        {
            if(_doBeforeUndoRedo!=null)
                _doBeforeUndoRedo();

            _childActions.ForEachReverse(action => action.CallUndo());

            if (_doAfterUndoRedo != null)
                _doAfterUndoRedo();
        }

        /// <summary>
        /// Executes the underlying redo command.
        /// </summary>
        public override void CallRedo()
        {
            if (_doBeforeUndoRedo != null)
                _doBeforeUndoRedo();

            _childActions.ForEach(action => action.CallRedo());

            if (_doAfterUndoRedo != null)
                _doAfterUndoRedo();
        }

        /// <summary>
        /// Adds a new edit entry to the current undo stack. This call will ignore
        /// the multiple undo/redo action request.
        /// </summary>
        /// <param name="editCommand">Specifies the entry to push onto the undo stack.</param>
        internal override void AddEditToCurrent(IUndoEditCommand editCommand)
        {
            
            _childActions.Add(editCommand);
            SetElementCounts(editCommand, editCommand.ElementName ?? string.Empty, editCommand.Cause ?? editCommand.Type.ToString(), _childElementNames, _childActionCauses);
        }

        private void SetElementCounts(IUndoEditCommand editCommand, string element, string cause, Dictionary<string, int> childElementNames, Dictionary<string, int> childActionCauses)
        {
            MultipleTrackingUndoCommand multipleCommand = editCommand as MultipleTrackingUndoCommand; ;
            if( multipleCommand !=null )
            {
                foreach(var command in _childActions)
                {
                    multipleCommand.SetElementCounts(command, command.ElementName, command.Cause, childElementNames, childActionCauses);
                }
            }
            else
            {
                CountElement(element, childElementNames);
                CountElement(cause, childActionCauses);    
            }
        }

        private void CountElement(string element, Dictionary<string, int> childDictionary)
        {
            if (!childDictionary.ContainsKey(element))
            {
                childDictionary[element] = 1;
            }
            else
            {
                childDictionary[element] = childDictionary[element] + 1;
            }
        }

        /// <summary>
        /// Creates a new instance of an UndoMultipleTrackingBase object.
        /// </summary>
        /// <param name="actionName">Name of the underlying undo/redo element.</param>
        /// <param name="doBeforeUndoRedo">Specifies the action to be executed before performing undo/redo.</param>
        /// <param name="doAfterUndoRedo">Specifies the action to be executed before performing undo/redo.</param>
        /// <returns>Returns tue created object.</returns>
        protected override UndoMultipleTrackingCommandBase CreateMultipleTrackingCommand(string actionName, Action doBeforeUndoRedo, Action doAfterUndoRedo)
        {
            return new MultipleTrackingUndoCommand(actionName, doBeforeUndoRedo, doAfterUndoRedo);
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}